<?php
/**
 * @fileoverview
 * CSV文件解析的驱动程序
 */

setlocale(LC_ALL, 'en_US.UTF-8');

if (!function_exists('fputcsv')) {
  function fputcsv(&$handle, $fields = array(), $delimiter = ',', $enclosure = '"') {
    $str = '';
    $escape_char = '\\';
    foreach ($fields as $value) {
      if (strpos($value, $delimiter) !== false ||
          strpos($value, $enclosure) !== false ||
          strpos($value, "\n") !== false ||
          strpos($value, "\r") !== false ||
          strpos($value, "\t") !== false ||
          strpos($value, ' ') !== false) {
        $str2 = $enclosure;
        $escaped = 0;
        $len = strlen($value);
        for ($i=0;$i<$len;$i++) {
          if ($value[$i] == $escape_char) {
            $escaped = 1;
          } else if (!$escaped && $value[$i] == $enclosure) {
            $str2 .= $enclosure;
          } else {
            $escaped = 0;
          }
          $str2 .= $value[$i];
        }
        $str2 .= $enclosure;
        $str .= $str2.$delimiter;
      } else {
        $str .= $value.$delimiter;
      }
    }
    $str = substr($str,0,-1);
    $str .= "\n";
    return fwrite($handle, $str);
  }
}

class CSV {

	/**
	 * 文件句柄
	 * @type resource
	 */
	var $fh;

	/**
	 * 临时文件句柄
	 * @type resource
	 */
	var $tmp_fh;

	/**
	 * 文件名
	 */
	var $filename;

	/**
	 * 临时文件名
	 */
	var $tmp_filename;

	/**
	 * 读模式/写模式
	 */
	var $is_update;

	/**
	 * 映射表，需要重载
	 * @type array
	 */
	var $map = array();

	function CSV($filename) {
		$this->__constructor($filename);
	}

	function __constructor($filename) {
		$this->filename = $filename;
	}

	/**
	 * 打开CSV文件
	 * @param {bool} $update 是否以写模式打开文件
	 * @type bool
	 * @return 成功与否
	 * @private
	 */
	function _open($update=false) {

		$this->is_update = $update;

		if ($update) {

			// 生成临时文件名
			$this->tmp_filename = $this->filename . ".tmp." . time() . rand();

			// 打开原文件
			$this->fh = @fopen($this->filename, "r");

			// 打开临时文件
			$this->tmp_fh = fopen($this->tmp_filename, "w");

			if (!$this->tmp_fh) {
				if ($this->fh) {
					fclose($this->fh);
					$this->fh = null;
				}
				return false;
			}

		} else {

			// 打开CSV数据文件
			$this->fh = fopen($this->filename, "r");
			if (!$this->fh) {
				return false;
			}
		}
		return true;
	}


	/**
	 * 关闭CSV文件
	 * @param {bool} $update 是否真的要更新文件(false则表明放弃写操作)
	 * @private
	 */
	function _close($update=true) {

		// 首先关闭文件句柄
		if ($this->fh) {
			fclose($this->fh);
		}

		// 如果是写模式
		if ($this->is_update) {
			// 关闭临时文件
			fclose($this->tmp_fh);

			// 如果没有取消写操作，则用临时文件覆盖原文件
			if ($update) {
				if (!@rename($this->tmp_filename, $this->filename)) {
					unlink($this->filename);
					rename($this->tmp_filename, $this->filename);
				}
			} else {
				// 否则，取消写操作时，删除临时文件
				unlink($this->tmp_filename);
			}
		}
	}

	function _get() {
		if ($this->fh) {
			return fgetcsv($this->fh, 4096, ',');
		} else {
			return false;
		}
	}

	function _put($row) {
		if ($this->tmp_fh) {
			fputcsv($this->tmp_fh, $row);
		}
	}


	/**
	 * 读取指定id的一行
	 * @param {number} $id 指定的id
	 * @type array
	 * @return 指定行的内容
	 */
	function read($id) {
		if (!$this->_open()) return false;
		while ($row = $this->_get()) {
			if ($row[0] == $id) {
				break;
			}
		}
		$this->_close();
		return $row;
	}

	/**
	 * 读取所有行
	 * @type array
	 * @return 二维数组，所有行的内容
	 */
	function read_all() {
		if (!$this->_open()) return false;
		$result = array();
		while ($row = $this->_get()) {
			array_push($result, $row);
		}
		$this->_close();
		return $result;
	}

	/**
	 * 获取id的最大值
	 * @return number
	 */
	function max_id() {
		$max = 0;
		if ($this->_open()) {
			while ($row = $this->_get()) {
				if ($row[0] > $max) {
					$max = $row[0];
				}
			}
			$this->_close();
		}
		return $max;
	}

	/**
	 * 更新指定id的一行
	 * @param {number} $id 要更新的行的id
	 * @param {array} $data 要更新的数据
	 */
	function write($id, $data) {
		if (!$this->_open(true)) return false;
		while ($row = $this->_get()) {
			if ($row[0] == $id) {
				$row = $data;
				$data = false;
			}
			$this->_put($row);
		}
		if ($data) {
			$this->_put($data);
		}
		$this->_close();
	}

	/**
	 * 更新所有行
	 * @param {array} $data 二维数组，要更新的数据
	 */
	function write_all($data) {
		if (!$this->_open(true)) return false;
		while ($row = array_shift($data)) {
			$this->_put($row);
		}
		$this->_close();
	}

	/**
	 * 将CSV数组转换为对象
	 */
	function csv2obj($data) {
		if (!$data) return false;

		$result = array();

		for ($i = 0; $i < count($this->map); $i++) {
			$result[$this->map[$i]] = $data[$i];
		}

		return $result;
	}


	/**
	 * 将对象转换为CSV数组
	 */
	function obj2csv($data) {
		if (!$data) return false;

		$result = array();

		for ($i = 0; $i < count($this->map); $i++) {
			$idx = $this->map[$i];
			$content = empty($data[$idx]) ? "" : $data[$idx];
			array_push($result, $content);
		}

		return $result;
	}
}

?>
